home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / reve / in.reved.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  16.1 KB  |  669 lines

  1.  
  2. /*  @(#)in.reved.c 1.5 91/11/13
  3.  *
  4.  *  Reve network daemon for remote games with user@host
  5.  *  These routines are based on those found in the BSD in.talkd program,
  6.  *  which is:
  7.  *
  8.  *  Copyright (c) 1983 Regents of the University of California.
  9.  *  All rights reserved.
  10.  *
  11.  *  Redistribution and use in source and binary forms are permitted
  12.  *  provided that: (1) source distributions retain this entire copyright
  13.  *  notice and comment, and (2) distributions including binaries display
  14.  *  the following acknowledgement:  ``This product includes software
  15.  *  developed by the University of California, Berkeley and its contributors''
  16.  *  in the documentation or other materials provided with the distribution
  17.  *  and in all advertising materials mentioning features or use of this
  18.  *  software. Neither the name of the University nor the names of its
  19.  *  contributors may be used to endorse or promote products derived
  20.  *  from this software without specific prior written permission.
  21.  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  22.  *  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  23.  *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  24.  */
  25.  
  26. #include "reve.h"
  27. #include <errno.h>
  28. #include <syslog.h>
  29. #include <utmp.h>
  30. #include <sgtty.h>
  31. #include <sys/stat.h>
  32. #include <sys/ioctl.h>
  33. #include <sys/socket.h>
  34. #include <sys/time.h>
  35. #include <sys/wait.h>
  36. #include <netinet/in.h>
  37. #include <netdb.h>
  38. #include "ctl.h"
  39.  
  40. #define  MAX_ID        16000                /* << 2^15 so no sign troubles. */
  41. #define  NIL           ((TABLE_ENTRY *) 0)
  42. #define  N_LINES       5
  43. #define  N_CHARS       120
  44. #define  SMAX(a, b)    ((a) > (b) ? (a) : (b))
  45. #define  SWAPSHORT(a)  (((a << 8) | ((unsigned short) a >> 8)) & 0xffff)
  46. #define  SWAPLONG(a)   ((SWAPSHORT(a)<<16) | (SWAPSHORT(((unsigned) a >> 16))))
  47.  
  48. extern char *malloc() ;
  49. extern int errno ;
  50.  
  51. struct hostent *gethostbyaddr() ;
  52. struct timeval tp ;
  53.  
  54. typedef struct table_entry TABLE_ENTRY ;
  55.  
  56. struct table_entry {
  57.  CTL_MSG request ;
  58.  long time ;
  59.  TABLE_ENTRY *next ;
  60.  TABLE_ENTRY *last ;
  61. } ;
  62.  
  63. TABLE_ENTRY *table = NIL ;
  64.  
  65. CTL_MSG *find_match(), *find_request(), request, swapmsg() ;
  66. CTL_RESPONSE response ;
  67.  
  68. char debugname[MAXLINE] ;
  69. char hostname[MAXLINE] ;
  70.  
  71. FILE *dfd = stdout ;
  72.  
  73. int debug  = 0 ;
  74. int nofork = 0 ;
  75.  
  76.  
  77. /*ARGSUSED*/
  78. main(argc, argv)
  79. int argc ;
  80. char *argv[] ;
  81. {
  82.   struct sockaddr_in from ;
  83.   int from_size ;
  84.   int cc ;
  85.   int name_length = sizeof(hostname) ;
  86.   struct timeval tv ;
  87.  
  88. #ifdef NO_43SELECT
  89.   int rfds ;
  90. #else
  91.   fd_set rfds ;
  92. #endif /*NO_43SELECT*/
  93.  
  94.   if (debug)
  95.     {
  96.       SPRINTF(debugname, "/tmp/reved.debug%D", getpid()) ;
  97.       dfd = fopen(debugname, "w") ;
  98.     }
  99.  
  100.   openlog("reved", LOG_PID, LOG_DAEMON) ;
  101.  
  102.   gethostname(hostname, &name_length) ;
  103.  
  104.   for (;;)
  105.     {
  106.       tv.tv_sec  = MAX_LIFE ;
  107.       tv.tv_usec = 0 ;
  108.  
  109. #ifdef NO_43SELECT
  110.       rfds = 1 << 0 ;
  111. #else
  112.       FD_ZERO(&rfds) ;
  113.       FD_SET(0, &rfds) ;
  114. #endif /*NO_43SELECT*/
  115.  
  116. #ifdef NO_43SELECT
  117.       if (select(32, &rfds, 0, 0, &tv) <= 0)
  118. #else
  119.       if (select(FD_SETSIZE, &rfds, (fd_set *) 0, (fd_set *) 0, &tv) <= 0)
  120. #endif /*NO_43SELECT*/
  121.         exit(0) ;
  122.  
  123.       from_size = sizeof(from) ;
  124.       cc = recvfrom(0, (char *) &request, sizeof(request), 0,
  125.                     &from, &from_size) ;
  126.  
  127.       if (cc != sizeof(request))
  128.         {
  129.           if (cc < 0 && errno != EINTR)
  130.             syslog(LOG_ERR, "recvfrom: %m") ;
  131.         }
  132.       else
  133.         {
  134.           if (debug) FPRINTF(dfd, "Request received : \n") ;
  135.           if (debug) print_request(&request) ;
  136.  
  137.           request = swapmsg(request) ;
  138.           process_request(&request, &response) ;
  139.  
  140.           if (debug) FPRINTF(dfd, "Response sent : \n") ;
  141.           if (debug) print_response(&response) ;
  142.  
  143. /* Can block here. */
  144.  
  145.           cc = sendto(0, (char *) &response, sizeof(response), 0,
  146.                       &request.ctl_addr, sizeof(request.ctl_addr)) ;
  147.  
  148.           if (cc != sizeof(response)) syslog(LOG_ERR, "sendto: %m") ;
  149.         }    
  150.     }    
  151. }
  152.  
  153.  
  154. /*  Because the tty driver insists on attaching a terminal-less process to
  155.  *  any terminal that it writes on, we must fork a child to protect ourselves.
  156.  */
  157.  
  158. announce(request, remote_machine)
  159. CTL_MSG *request ;
  160. char *remote_machine ;
  161. {
  162.   int pid, val, status ;
  163.  
  164.   if (nofork) return(announce_proc(request, remote_machine)) ;
  165.  
  166.   if (pid = fork())
  167.     {
  168.  
  169. /* We are the parent, so wait for the child. */
  170.  
  171.       if (pid == -1) return(FAILED) ;    /* The fork failed. */
  172.  
  173.       do
  174.         {
  175.           val = wait(&status) ;
  176.           if (val == -1)
  177.             {
  178.               if (errno == EINTR) continue;
  179.               else
  180.                 {
  181.                   syslog(LOG_ERR, "wait: %m") ;   /* Shouldn't happen. */
  182.                   return(FAILED) ;
  183.                 }
  184.             }    
  185.         }
  186.       while (val != pid) ;
  187.  
  188.       if (status & 0377 > 0) return(FAILED) ;  /* Killed by some signal. */
  189.  
  190. /* Get the second byte, this is the exit/return code. */
  191.  
  192.       return((status >> 8) & 0377) ;
  193.  
  194.     }
  195.   else                  /* we are the child, go and do it. */
  196.     _exit(announce_proc(request, remote_machine)) ;
  197. }
  198.  
  199.  
  200. /*  See if the user is accepting messages. If so, announce that
  201.  *  a talk is requested.
  202.  */
  203.  
  204. announce_proc(request, remote_machine)
  205. CTL_MSG *request ;
  206. char *remote_machine ;
  207. {
  208.   int pid, status ;
  209.   char full_tty[32] ;
  210.   FILE *tf ;
  211.   struct stat stbuf ;
  212.  
  213.   SPRINTF(full_tty, "/dev/%s", request->r_tty) ;
  214.  
  215.   if (access(full_tty, 0) != 0) return(FAILED) ;
  216.   if ((tf = fopen(full_tty, "w")) == NULL) return(PERMISSION_DENIED) ;
  217.  
  218. /*  Open gratuitously attaches the reved to any tty it opens, so disconnect
  219.  *  us from the tty before we catch a signal.
  220.  */
  221.  
  222. #ifndef hpux
  223.   IOCTL(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0) ;
  224. #endif /*hpux*/
  225.  
  226.   if (fstat(fileno(tf), &stbuf) < 0) return(PERMISSION_DENIED) ;
  227.   if ((stbuf.st_mode & 020) == 0)    return(PERMISSION_DENIED) ;
  228.  
  229.   print_mesg(tf, request, remote_machine) ;
  230.   FCLOSE(tf) ;
  231.   return(SUCCESS) ;
  232. }
  233.  
  234.  
  235. delete(ptr)             /* Classic delete from a double-linked list. */
  236. TABLE_ENTRY *ptr ;
  237. {
  238.   if (debug) FPRINTF(dfd, "Deleting : ") ;
  239.   if (debug) print_request(&ptr->request) ;
  240.  
  241.        if (table == ptr)     table = ptr->next ;
  242.   else if (ptr->last != NIL) ptr->last->next = ptr->next ;
  243.  
  244.   if (ptr->next != NIL) ptr->next->last = ptr->last ;
  245.   (void) free((char *) ptr) ;
  246. }
  247.  
  248.  
  249. delete_invite(id_num)     /* Delete the invitation with id 'id_num'. */
  250. int id_num ;
  251. {
  252.   TABLE_ENTRY *ptr ;
  253.  
  254.   ptr = table ;
  255.  
  256.   if (debug) FPRINTF(dfd, "Entering delete_invite with %d\n", id_num) ;
  257.  
  258.   while (ptr != NIL && ptr->request.id_num != id_num)
  259.     {
  260.       if (debug) print_request(&ptr->request) ;
  261.       ptr = ptr->next ;
  262.     }
  263.  
  264.   if (ptr != NIL)
  265.     {
  266.       delete(ptr) ;
  267.       return(SUCCESS) ;
  268.     }
  269.   return(NOT_HERE) ;
  270. }
  271.  
  272.  
  273. do_announce(request, response)
  274. CTL_MSG *request ;
  275. CTL_RESPONSE *response ;
  276. {
  277.   struct hostent *hp ;
  278.   CTL_MSG *ptr ;
  279.   int result ;
  280.  
  281. /* See if the user is logged in. */
  282.  
  283.   result = find_user(request->r_name, request->r_tty) ;
  284.                  
  285.   if (result != SUCCESS)
  286.     {
  287.       response->answer = result ;
  288.       return ;
  289.     }            
  290.  
  291.   hp = gethostbyaddr(&request->ctl_addr.sin_addr,
  292.                      sizeof(struct in_addr), AF_INET) ;
  293.           
  294.   if (hp == (struct hostent *) 0)
  295.     {
  296.       response->answer = MACHINE_UNKNOWN ;
  297.       return ;
  298.     }    
  299.  
  300.   ptr = find_request(request) ;
  301.   if (ptr == (CTL_MSG *) 0)
  302.     {
  303.       insert_table(request, response) ;
  304.       response->answer = announce(request, hp->h_name) ;
  305.     }
  306.   else if (request->id_num > ptr->id_num)
  307.     {
  308.  
  309. /*  This is an explicit re-announce, so update the id_num
  310.  *  field to avoid duplicates and re-announce the reve request.
  311.  */
  312.  
  313.       ptr->id_num = response->id_num = new_id() ;
  314.       response->answer = announce(request, hp->h_name) ;
  315.     }
  316.   else
  317.     {
  318.       response->id_num = ptr->id_num ;   /* Duplicated request, ignore it. */
  319.       response->answer = SUCCESS ;
  320.     }
  321.   return ;
  322. }
  323.  
  324.  
  325. /*  Look in the table for an invitation that matches the current
  326.  *  request looking for an invitation.
  327.  */
  328.  
  329. CTL_MSG *
  330. find_match(request)
  331. CTL_MSG *request ;
  332. {
  333.   TABLE_ENTRY *ptr ;
  334.   long current_time ;
  335.  
  336.   gettimeofday(&tp, (struct timezone *) 0) ;
  337.   current_time = tp.tv_sec ;
  338.  
  339.   ptr = table ;
  340.  
  341.   if (debug)
  342.     {
  343.       FPRINTF(dfd, "Entering Look-Up with : \n") ;
  344.       print_request(request) ;
  345.     }     
  346.  
  347.   while (ptr != NIL)
  348.     {
  349.       if ((ptr->time - current_time) > MAX_LIFE)
  350.         {
  351.  
  352. /* The entry is too old. */
  353.  
  354.           if (debug) FPRINTF(dfd, "Deleting expired entry : \n") ;
  355.           if (debug) print_request(&ptr->request) ;
  356.           delete(ptr) ;
  357.           ptr = ptr->next ;
  358.           continue ;
  359.         } 
  360.  
  361.       if (debug) print_request(&ptr->request) ;
  362.  
  363.       if (strcmp(request->l_name, ptr->request.r_name) == 0 &&
  364.           strcmp(request->r_name, ptr->request.l_name) == 0 &&
  365.           ptr->request.type == LEAVE_INVITE)
  366.         return(&ptr->request) ;
  367.  
  368.       ptr = ptr->next ;
  369.     }
  370.   return((CTL_MSG *) 0) ;
  371. }
  372.  
  373.  
  374. /*  Look for an identical request, as opposed to a complimentary
  375.  *  one as find_match does.
  376.  */
  377.  
  378. CTL_MSG *
  379. find_request(request)
  380. CTL_MSG *request ;
  381. {
  382.   TABLE_ENTRY *ptr ;
  383.   long current_time ;
  384.  
  385.   gettimeofday(&tp, (struct timezone *) 0) ;
  386.   current_time = tp.tv_sec ;
  387.  
  388. /*  See if this is a repeated message, and check for out of date entries
  389.  *  in the table while we are it.
  390.  */
  391.  
  392.   ptr = table ;
  393.  
  394.   if (debug)
  395.     {
  396.       FPRINTF(dfd, "Entering find_request with : \n") ;
  397.       print_request(request) ;
  398.     }     
  399.  
  400.   while (ptr != NIL)
  401.     {
  402.       if ((ptr->time - current_time) > MAX_LIFE)
  403.         {
  404.  
  405. /* The entry is too old. */
  406.  
  407.           if (debug) FPRINTF(dfd, "Deleting expired entry : \n") ;
  408.           if (debug) print_request(&ptr->request) ;
  409.           delete(ptr) ;
  410.           ptr = ptr->next ;
  411.           continue ;
  412.         }
  413.  
  414.       if (debug) print_request(&ptr->request) ;
  415.  
  416.       if (strcmp(request->r_name, ptr->request.r_name) == 0 &&
  417.           strcmp(request->l_name, ptr->request.l_name) == 0 &&
  418.           request->type == ptr->request.type                &&
  419.           request->pid == ptr->request.pid)
  420.         {
  421.  
  422. /* Update the time if we 'touch' it. */
  423.  
  424.           ptr->time = current_time ;
  425.           return(&ptr->request) ;
  426.         }
  427.       ptr = ptr->next ;
  428.     }
  429.   return((CTL_MSG *) 0) ;
  430. }
  431.  
  432.  
  433. find_user(name, tty)       /* Search utmp for the local user. */
  434. char *name, *tty ;
  435. {
  436.   struct utmp ubuf ;
  437.   int fd ;
  438.  
  439.   if ((fd = open("/etc/utmp", 0)) == -1)
  440.     {
  441.       syslog(LOG_ERR, "Can't open /etc/utmp: %m") ;
  442.       return(FAILED) ;
  443.     }   
  444.  
  445.   while (read(fd, (char *) &ubuf, sizeof(ubuf)) == sizeof(ubuf))
  446.     {
  447.       if (strncmp(ubuf.ut_name, name, sizeof(ubuf.ut_name)) == 0)
  448.         {
  449.           if (*tty == '\0')
  450.             {
  451.  
  452. /* No particular tty was requested. */
  453.  
  454.               STRCPY(tty, ubuf.ut_line) ;
  455.               CLOSE(fd) ;
  456.               return(SUCCESS) ;
  457.             }
  458.           else if (strcmp(ubuf.ut_line, tty) == 0)
  459.             {
  460.               CLOSE(fd) ;
  461.               return(SUCCESS) ;
  462.             }
  463.         }
  464.     }
  465.   CLOSE(fd) ;
  466.   return(NOT_HERE) ;
  467. }
  468.  
  469.  
  470. insert_table(request, response)
  471. CTL_MSG *request ;
  472. CTL_RESPONSE *response ;
  473. {
  474.   TABLE_ENTRY *ptr ;
  475.   long current_time ;
  476.  
  477.   gettimeofday(&tp, (struct timezone *) 0) ;
  478.   current_time = tp.tv_sec ;
  479.  
  480.   response->id_num = request->id_num = new_id() ;
  481.              
  482. /* Insert a new entry into the top of the list. */
  483.              
  484.   ptr = (TABLE_ENTRY *) malloc(sizeof(TABLE_ENTRY)) ;
  485.  
  486.   if (ptr == NIL) syslog(LOG_ERR, "malloc in insert_table") ;
  487.  
  488.   ptr->time = current_time ;
  489.   ptr->request = *request ;
  490.  
  491.   ptr->next = table ;
  492.   if (ptr->next != NIL) ptr->next->last = ptr ;
  493.   ptr->last = NIL ;
  494.   table     = ptr ;
  495. }
  496.  
  497.  
  498. new_id()     /* Generate a unique non-zero sequence number. */
  499. {
  500.   static int current_id = 0 ;
  501.  
  502.   current_id = (current_id + 1) % MAX_ID ;
  503.  
  504. /* 0 is reserved, helps to pick up bugs. */
  505.  
  506.   if (current_id == 0) current_id = 1 ;
  507.   return(current_id) ;
  508. }
  509.  
  510.  
  511. /*  Build a block of characters containing the message.
  512.  *  It is sent blank filled and in a single block to try to keep the message
  513.  *  in one piece if the recipient in in vi at the time.
  514.  */
  515.  
  516. print_mesg(tf, request, remote_machine)
  517. FILE *tf ;
  518. CTL_MSG *request ;
  519. char *remote_machine ;
  520. {
  521.   struct timeval clock ;
  522.   struct tm *localclock, *localtime() ;
  523.   char big_buf[N_LINES*N_CHARS], line_buf[N_LINES][N_CHARS] ;
  524.   char *bptr, *lptr ;
  525.   int i, j, max_size, sizes[N_LINES] ;
  526.  
  527.   i = 0 ;
  528.   max_size = 0 ;
  529.  
  530.   gettimeofday(&clock, (struct timezone *) NULL) ;
  531.   localclock = localtime(&clock.tv_sec) ;
  532.  
  533.   SPRINTF(line_buf[i], " ") ;
  534.  
  535.   sizes[i] = strlen(line_buf[i]) ;
  536.   max_size = SMAX(max_size, sizes[i]) ;
  537.   i++ ;
  538.  
  539.   SPRINTF(line_buf[i], "Message from Reve_Daemon@%s at %d:%02d ...",
  540.           hostname, localclock->tm_hour , localclock->tm_min) ;
  541.  
  542.   sizes[i] = strlen(line_buf[i]) ;
  543.   max_size = SMAX(max_size, sizes[i]) ;
  544.   i++ ;
  545.  
  546.   SPRINTF(line_buf[i], "reve: connection requested by %s@%s.",
  547.           request->l_name, remote_machine) ;
  548.  
  549.   sizes[i] = strlen(line_buf[i]) ;
  550.   max_size = SMAX(max_size, sizes[i]) ;
  551.   i++ ;
  552.  
  553.   SPRINTF(line_buf[i], "reve: respond with:  reve -opponent %s@%s",
  554.           request->l_name, remote_machine) ;
  555.  
  556.   sizes[i] = strlen(line_buf[i]) ;
  557.   max_size = SMAX(max_size, sizes[i]) ;
  558.   i++ ;
  559.  
  560.   SPRINTF(line_buf[i], " ") ;
  561.  
  562.   sizes[i] = strlen(line_buf[i]) ;
  563.   max_size = SMAX(max_size, sizes[i]) ;
  564.   i++ ;
  565.  
  566.   bptr      = big_buf ;
  567.   *(bptr++) = '\007' ;         /* Send something to wake them up. */
  568.   *(bptr++) = '\r' ;           /* Add a \r in case of raw mode. */
  569.   *(bptr++) = '\n' ;
  570.   for (i = 0; i < N_LINES; i++)
  571.     {
  572.  
  573. /* Copy the line into the big buffer. */
  574.  
  575.       lptr = line_buf[i] ;
  576.       while (*lptr != '\0') *(bptr++) = *(lptr++) ;
  577.  
  578. /* Pad out the rest of the lines with blanks. */
  579.  
  580.       for (j = sizes[i]; j < max_size + 2; j++) *(bptr++) = ' ' ;
  581.  
  582.       *(bptr++) = '\r' ;      /* Add a \r in case of raw mode. */
  583.       *(bptr++) = '\n' ;
  584.     }
  585.   *bptr = '\0' ;
  586.  
  587.   FPRINTF(tf, big_buf) ;
  588.   FFLUSH(tf) ;
  589. #ifndef hpux
  590.   IOCTL(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0) ;
  591. #endif /*hpux*/
  592. }
  593.  
  594.  
  595. print_request(request)
  596. CTL_MSG *request ;
  597. {
  598.   FPRINTF(dfd, "type is %d, l_user %s, r_user %s, r_tty %s\n\tid = %d\n",
  599.                request->type, request->l_name, request->r_name,
  600.                request->r_tty, request->id_num) ;
  601.   FFLUSH(dfd) ;
  602. }
  603.  
  604.  
  605. print_response(response)
  606. CTL_RESPONSE *response;
  607. {
  608.   FPRINTF(dfd, "type is %d, answer is %d, id = %d\n\n", response->type,
  609.                response->answer, response->id_num) ;
  610.   FFLUSH(dfd) ;
  611. }
  612.  
  613.  
  614. process_request(request, response)
  615. CTL_MSG *request ;
  616. CTL_RESPONSE *response ;
  617. {
  618.   CTL_MSG *ptr ;
  619.  
  620.   response->type   = request->type ;
  621.   response->id_num = 0 ;
  622.  
  623.   switch (request->type)
  624.     {
  625.       case ANNOUNCE     : do_announce(request, response) ;
  626.                           break ;
  627.       case LEAVE_INVITE : ptr = find_request(request) ;
  628.                           if (ptr != (CTL_MSG *) 0)
  629.                             {
  630.                               response->id_num = ptr->id_num ;
  631.                               response->answer = SUCCESS ;
  632.                             }
  633.                           else insert_table(request, response) ;
  634.                           break ;
  635.       case LOOK_UP      : ptr = find_match(request) ;
  636.                           if (ptr != (CTL_MSG *) 0)
  637.                             {
  638.                               response->id_num = ptr->id_num ;
  639.                               response->addr   = ptr->addr ;
  640.                               response->dtype  = ptr->dtype ;
  641.                               response->answer = SUCCESS ;
  642.                             }
  643.                           else response->answer = NOT_HERE ;
  644.                           break ;
  645.       case DELETE       : response->answer = delete_invite(request->id_num) ;
  646.                           break ;
  647.       default           : response->answer = UNKNOWN_REQUEST ;
  648.     }
  649. }
  650.  
  651.  
  652. CTL_MSG
  653. swapmsg(req)            /* Heuristic to detect if need to swap bytes. */
  654. CTL_MSG req ;
  655. {
  656.   CTL_MSG swapreq ;
  657.  
  658.   if (req.ctl_addr.sin_family == SWAPSHORT(AF_INET))
  659.     {
  660.       swapreq                     = req ;
  661.       swapreq.id_num              = SWAPLONG(req.id_num) ;
  662.       swapreq.pid                 = SWAPLONG(req.pid) ;
  663.       swapreq.addr.sin_family     = SWAPSHORT(req.addr.sin_family) ;
  664.       swapreq.ctl_addr.sin_family = SWAPSHORT(req.ctl_addr.sin_family) ;
  665.       return(swapreq) ;
  666.     }
  667.   else return(req) ;
  668. }
  669.